JSX 문법과 사용
✒️ 2025-05-28 09:35 내용 수정
JSX 문법
Javascript 문법의 확장으로 React에서 사용함
- React에서는 JSX문법으로 작성하며, App.js에 기본 형식이 작성되어 있다.
- 참고 자료 : React legacy JSX 소개, React JSX 소개
- 공식 설명에 따르면 마크업과 로직을 넣어 기술을 인위적으로 분리하는 대신 둘 다 포함하는 Component라는 유닛으로 관심사를 분리한다.
- 중괄호 "
{}"에는 Javascript 표현식을 작성할 수 있다.- JSP 태그#스크립트 태그와 유사한 느낌이다.
- 예제는 공식 페이지에 있는 내용이다.
function test() {
return (
<div>
<h1>React Test</h1>
</div>
)
}
const user = {
firstName : 'Tony',
lastName : 'Stark'
}
function nameFormat(user) {
return user.firstName + ' ' + user.lastName;
}
- 컴파일 후에는 JSX 표현식이 Javascript 함수 호출이 되고, Javascript 객체로 인식되기 때문에 if문이나 for문 내에서 JSX를 사용할 수 있다.
let status = true;
function isTired(status) {
if (status) {
return <span>Go to sleep...</span>;
} else {
return <span>Play game!</span>;
}
}
function App() {
function drawRender() {
let result = [];
for(let i = 0; i < 10; i++) {
result.push(<span key={i}></span>);
}
}
return (
<>
drawRender();
// <span key={0}></span> 부터 <span key={9}></span> 까지가 생성된다
</>
)
}
- Attribute를 사용시 문자열 리터럴 또는 Javascript 표현식을 포함하여 작성한다.
- Attribute는 Camel 스타일(표기 규칙#2. Camel 표기법)으로 사용한다.
let box = <div className="box"></div>;
const user = {name:'Arther Morgan'}
let input = <input type="text" value={user.name}>{user.name}</input>
- property를 여러 개 적용 시 구분 기호는 ","를 사용하고, Javascript 객체 형태로 작성 한다.
<p style={ {color:'red', fontSize:'30px'} }>
JSX 작성
1. 태그 작성
- HTML에 작성할 태그는
function Name()형식 안에 return 값으로 작성한다.- return되는 태그는 1개만 가능하므로, 여러 개의 태그를 넣고 싶다면 이들을 감싸는 큰 태그로 묶어야 한다.
function App() {
return(
// retunr은 태그 1개만 가능
<div className="container">
// 태그 내용
</div>
)
}
2. 변수 선언
- 사용할 변수는 함수 내에 return 전에 선언한다.
function App() {
let test = '테스트'; // return 전에 선언
let section = 'section';
return(
<div className={section}>
<h1>{test}</h1>
</div>
)
}
3. 이벤트 추가
- 이벤트 추가 시 property에 onclick 등은 onClick으로 사용한다.
- 이벤트에는 함수를 넣을 때 중괄호 "
{}" 안에 넣어야 하며, 함수를 넣거나, 함수 표현식(익명 함수, 화살표 함수)를 바로 작성할 수 있다.
- 이벤트에는 함수를 넣을 때 중괄호 "
function App() {
function test() {
console.log('test');
}
return(
<div>
<h1> <span onClick={test}>Click Me!</span> </h1>
<h1> <span onClick={() => {
console.log('Click Me too!');
}}>
Click Me too!</span>
</h1>
</div>
)
}
4. useState
- 자주 변경되는 내용(ex: 좋아요 누를 때 숫자 증가 등)은 useState를 사용하여 선언한다.
- Hook과 State 내용을 참고.
- 웹 페이지 새로 고침 없이 값이 자주 변경되는 내용들을 useState로 선언한다.
- 값의 상태를 바꾸는 함수를 사용하여 값을 변경할 수 있다.
- useState 안에는 단일 값과 배열이 들어갈 수 있으며, Distructuring(Distructuring) 되는 부분(왼쪽)에는 data와 change 함수의 이름을 작성한다.
- change 함수는 data가 배열일 경우 배열의 모든 값을 바꾼다.
- 후술할 배열 데이터 state 변경 방법 참고.
import {useState} from 'react'; // useState를 꼭 가져와야 한다.
function App() {
let test = '테스트'; // 정적 내용, 잘 안바뀌는 내용
// data, change 함수 순으로 작성
let [data, changeFunction] = useState('0'); // 동적 내용, 자주 바뀌는 내용
let [data2, changeFunction2] = useState(['값1', '값2', '값3']); // 배열도 가능하다
return(
<div>
// '테스트'가 출력된다
<h1>{test}</h1>
// data의 값인 0이 출력된다.
<p>{data}</p>
// data2(배열)의 각 요소 값이 출력된다.
<p>{data2[0]}, {data2[1]}, {data2[2]}</p>
</div>
)
}
- useState에서 변경되는 데이터를 처리하는 change 함수(setState 함수)는 이벤트 함수 내에서 호출한다.
- 예제는 아이콘을 누르면 likeHit이 1씩 증가하며, 페이지 새로고침 없이 바로 반영된다.
import {useState} from 'react';
function App() {
let [likeHit, likeHitChange] = useState(0);
function likeUp() { // 이벤트 처리 함수
likeHitChange(likeHit++); // useState의 data를 변경시킬 함수
}
return(
<div>
<h1> <span onClick={likeUp}>👍</span> {likeHit} </h1>
</div>
)
}
- 배열로 묶인 데이터를 수정할 때는 원본 배열을 깊은 복사한 새 배열에서 데이터를 수정하고, change 함수(setState 함수)로 배열 전체 내용을 수정 반영해야 한다.
- 원본 데이터를 직접 수정하는 방법도 있지만, 원본을 복사한 데이터를 수정하여 반영함으로써 변경 사항을 추적하기 쉽고, Undo/Redo와 같은 동작을 쉽게 수행할 수 있다.
- 참고 자료 : React 튜토리얼 : React 시작하기
/* eslint-disable */
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css';
import {useState} from 'react';
function App() {
let post = "2023년 개봉 영화"; // 정적 내용
// 내용 변경이 자주 일어나는 부분은 state에 값을 담아서 처리한다.
// UI 기능 개발, 사이트 동작 등 자주 변경되는 부분을 새로고침 없이 재 랜더링해준다.
let [title, titleChange] = useState(['존 윅', '가디언즈 오브 갤럭시', '스파이더맨']); // 동적 내용, html 재랜더링
let [likeHit, likeHitChange] = useState([0, 0, 0]);
function likeUp(e) {
let num = e.target.className; // 이벤트가 발생한 대상을 가져와 className을 읽어옴
let copy = [...likeHit]; // likeHit이라는 데이터의 배열을 깊은 복사
copy[num]++; // 배열의 특정 index 요소를 +1 수행
// 이렇게 하면 각기 다른 likeHit 요소들이 따로 +1 된다.
likeHitChange(copy); // function은 data 전체를 바꾸기 때문에 복사한 배열로 원본 배열 수정 적용
}
return (
<div className="App">
<header className="header">
<div className="container">
<h1 className="logo"><a href="#">BLOG</a></h1>
</div>
</header>
<section className="sec blog">
<div className="container">
<div class="row list">
<div className="item">
<h3> {title[0]} <span onClick={likeUp} className='0'>👍</span> {likeHit[0]} </h3>
<p>{post}</p>
</div>
<div className="item">
<h3>{title[1]} <span onClick={likeUp} className='1'>👍</span> {likeHit[1]} </h3>
<p>{post}</p>
</div>
<div className="item">
<h3>{title[2]} <span onClick={likeUp} className='2'>👍</span> {likeHit[2]} </h3>
<p>{post}</p>
</div>
</div>
</div>
</section>
</div>
);
}
export default App;
5. 함수 선언 및 사용
function App() {}을 선언해서 index.js에서 import하여 사용했던 것처럼, App.js에서 다른 함수를 사용하여 태그를 만들고 포함시킬 수 있다.
function App() {
let [detail, setDetail] = useState(false); // detail의 기본 상태는 false
// onClick을 다른 곳에 추가하여 아래 Detail이 보이거나 안보이게 설정할 수 있다.
return (
<div className="App">
<section className="sec">
<div className="container">
<div className='row'>
<h2>조회</h2>
{
// useState detail의 상태에 따라 보이거나 안보이게 설정
// <Detail></Detail>의 내용은 아래 함수의 return 값으로 반환되는 태그
(detail) ? <Detail></Detail>: null
}
</div>
</div>
</section>
</div>
);
}
function Detail() { // 함수 이름과 태그 이름이 동일함
return(
<div className='detail'>
<h3>제목</h3>
<p>날짜</p>
<p>상세내용</p>
</div>
);
}
6. props.children
- Component 태그 사이에 작성한 내용은 해당 Component에서 props.children이라는 속성으로 사용할 수 있다.
function App() {
return(
<Title>새로운 타이틀</Title>
)
}
function Title(props) {
console.log(props.children); // "새로운 타이틀"
return(
{/* "새로운 타이틀"이 자동으로 들어간다 */}
<h2 className="title">{props.children}</h2>
)
}
기타 활용
- 반복되는 구문 처리 : Array 객체의 map 메소드를 사용하여 비슷한 내용의 태그를 반복문으로 처리하여 한 번에 출력할 수 있다.
- 배열#Array 객체의 메소드의 map 메소드 참고.
/* eslint-disable */
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css';
import {useState} from 'react';
function App() {
let post = '2024년 발매 게임';
let [title, titleChange] = useState(['리썰 컴퍼니', '팰월드', '헬스테이트2', '라스트어포치']);
let [likeHit, likeHitChange] = useState([0, 0, 0, 0]);
let [detail, detailChange] = useState([false, false, false, false]);
function likeUp(e) {
let num = e.target.className; // 이 부분은 아래 활용 코드에서 더 간단하게 수정했다.
let copy = [...likeHit];
copy[num]++;
likeHitChange(copy);
}
function detailSwap(e) {
let num = e.target.className;
let copy = [...detail];
copy[num] = !copy[num];
detailChange(copy);
}
return (
<div className='App'>
<header className='header'>
<div className='container'>
<h1 className='logo'><a href="#">BLOG</a></h1>
</div>
</header>
<section className='section sec'>
<div className='container'>
<div className='row list'>
{
title.map((el, i) => { // map을 사용하여 배열의 요소와 인덱스로 새 배열을 반환
return(
// map을 사용하는 경우 자식 객체에 key가 필요하다는 로그가 뜬다.
// key={구분값}으로 자식마다 key를 배정해준다.
<div key={i} className='item'>
<div key={i*10} className='info'>
<h3>
<span onClick={detailSwap} className={i}>{el}</span>
<span onClick={likeUp} className={i} style={{cursor:'pointer'}}>❤</span> {likeHit[i]}
</h3>
<p>{post}</p>
</div>
{
(detail[i]) ? <Detail></Detail> : null
}
</div>
)
})
}
</div>
</div>
</section>
</div>
);
}
function Detail() {
return(
<div className='detail'>
<h3></h3>
<p>날짜</p>
<p>상세 내용</p>
</div>
)
}
export default App;
- 수업 때 사용한 css도 적용한다.
*{margin:0; padding:0; box-sizing: border-box;}
ul, ol, li{list-style: none;}
a{text-decoration: none;}
body{background-color: #efefef;}
.header{
width: 100%;
padding: 30px 0;
background-color: #444;
}
.header a{color:#fff;}
.sec{
width: 100%;
padding:50px 0;
}
.sec .item{
margin: 20px 0; padding: 30px;
background-color: #fff;
border-radius: 10px;
}
.detail{
padding: 30px;
background-color: #eee;
}
-
각 div에 배열로 지정해둔 이름들이 뜬다.
- 자료 조사하다 보니 리썰 컴퍼니는 2023년 말 발매였다. 2024년이 아니라..
- 자료 조사하다 보니 리썰 컴퍼니는 2023년 말 발매였다. 2024년이 아니라..
-
제목을 누르면 상세 내용을 볼 수 있는 div가 렌더링된다.
-
항목별로 하트 이모티콘을 누르면 카운트가 올라간다.
- function으로 만든 태그에 property를 만들어 데이터를 전달할 수 있다.
- Onclick에 index를 넘겨줄 때 화살표 함수로 표현해야 Too many re-renders 에러가 발생하지 않는다.
- Too many re-renders. React limits the number of renders to prevent an infinite loop. 참고.
/* eslint-disable */
import 'bootstrap/dist/css/bootstrap.min.css';
import './App.css';
import {useState} from 'react';
function App() {
let post = '2024년 발매 게임';
let [title, titleChange] = useState(['리썰 컴퍼니', '팰월드', '헬다이버즈2', '라스트에폭']);
// 새로 발매일과 관련된 배열을 만들어준다.
let [date, dateChange] = useState(['2023-10-24', '2024-01-19', '2024-02-08', '2024-01-19', '2024-02-21']);
let [likeHit, likeHitChange] = useState([0, 0, 0, 0]);
let [detail, detailChange] = useState([false, false, false, false]);
function likeUp(num) {
let copy = [...likeHit];
copy[num]++;
likeHitChange(copy);
}
function detailSwap(num) {
let copy = [...detail];
copy[num] = !copy[num];
detailChange(copy);
}
return (
<div className='App'>
<header className='header'>
<div className='container'>
<h1 className='logo'><a href="#">BLOG</a></h1>
</div>
</header>
<section className='section sec'>
<div className='container'>
<div className='row list'>
{
title.map((el, i) => { // map을 사용하여 배열의 요소와 인덱스로 새 배열을 반환
return(
<div key={i} className='item'>
<div key={i*10} className='info'>
<h3>
<span onClick={() => detailSwap(i)}>{el}</span>
<span onClick={() => likeUp(i)} style={{cursor:'pointer'}}>❤</span> {likeHit[i]}
</h3>
<p>{post}</p>
</div>
{ // <Detail>에 property를 추가하면 아래 function에서 받아와 사용할 수 있다.
(detail[i]) ? <Detail title={title} date={date} index={i}></Detail> : null
}
</div>
)
})
}
</div>
</div>
</section>
</div>
);
}
function Detail(props) {
// <Detail>에서 작성한 property는 객체로, 아래처럼 가져올 수 있음
let {title, date, index} = props;
return(
<div className='detail'>
<h3>{title[index]}</h3>
<p>{date[index]}</p>
<p>상세 내용</p>
</div>
)
}
export default App;
- 새로 만든 배열의 내용을 가져와 상세보기 창에 띄울 수 있다.